/**
 * Axelor Business Solutions
 *
 * Copyright (C) 2017 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.axelor.studio.service.builder;

import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.axelor.meta.db.MetaView;
import com.axelor.meta.loader.XMLViews;
import com.axelor.meta.schema.ObjectViews;
import com.axelor.meta.schema.views.AbstractView;
import com.axelor.meta.schema.views.AbstractWidget;
import com.axelor.meta.schema.views.Field;
import com.axelor.meta.schema.views.GridView;
import com.axelor.studio.db.ViewBuilder;
import com.axelor.studio.db.ViewItem;

/**
 * This service class generate GridView from ViewBuilder.
 * 
 * @author axelor
 *
 */
public class GridBuilderService {

	protected Logger log = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );

	/**
	 * Root method to access the service to get GridView from ViewBuilder
	 * 
	 * @param viewBuilder
	 *            ViewBuilder to process
	 * @return GridView generated by processing.
	 * @throws JAXBException
	 *             Exception returns in parsing xml.
	 */
	public GridView getView(ViewBuilder viewBuilder) throws JAXBException {

		List<ViewItem> viewItems = viewBuilder.getViewItemList();
		if (viewItems.isEmpty()) {
			return null;
		}

		GridView gridView = getGridView(viewBuilder);
		log.debug("Process grid view: {}", gridView.getName());

		Map<Integer, AbstractWidget> itemMap = new HashMap<Integer, AbstractWidget>();
		List<AbstractWidget> oldItems = gridView.getItems();
		if (oldItems != null) {
			extractNoneFieldItems(itemMap, oldItems);
		}

		sortFieldList(viewItems);

		addItems(itemMap, gridView, viewItems);

		return gridView;
	}

	/**
	 * Create gridView from parent view's xml
	 * 
	 * @param viewBuilder
	 *            ViewBuilder containing parent metaView.
	 * @return GridView created.
	 * @throws JAXBException
	 */
	private GridView getGridView(ViewBuilder viewBuilder) throws JAXBException {

		GridView grid = null;

		MetaView metaView = viewBuilder.getMetaView();
		
		String module = viewBuilder.getMetaModule().getName() + "-";
		String xmlId = module + viewBuilder.getName();
		
		if (metaView == null) {
			grid = new GridView();
			grid.setName(viewBuilder.getName());
			grid.setModel(viewBuilder.getModel());
			grid.setTitle(viewBuilder.getTitle());
			grid.setXmlId(xmlId);
			return grid;
		}

		ObjectViews objectViews = XMLViews.fromXML(metaView.getXml());
		List<AbstractView> views = objectViews.getViews();
		if (!views.isEmpty()) {
			grid = (GridView) views.get(0);
			grid.setXmlId(xmlId);
		}

		return grid;

	}

	/**
	 * Method extract non field items like button from grid items and put in
	 * itemMap.
	 * 
	 * @param itemMap
	 *            AbstractWidget map with index of widget in grid as key and
	 *            widget as value
	 * @param items
	 *            GridView items.
	 */
	private void extractNoneFieldItems(Map<Integer, AbstractWidget> itemMap,
			List<AbstractWidget> items) {

		Integer counter = 0;
		for (AbstractWidget abstractWidget : items) {

			if (!(abstractWidget instanceof Field)) {
				itemMap.put(counter, abstractWidget);
			}

			counter++;
		}

	}

	/**
	 * Method update grid view with new fields from viewFields for ViewBuilder.
	 * 
	 * @param itemMap
	 *            Map of existing non field view items of parent grid view.
	 * @param grid
	 *            GridView to update.
	 * @param viewFields
	 *            ViewFields of ViewBuilder.
	 */
	private void addItems(Map<Integer, AbstractWidget> itemMap, GridView grid,
			List<ViewItem> viewItems) {

		List<AbstractWidget> fields = new ArrayList<AbstractWidget>();

		for (ViewItem viewItem : viewItems) {

			Field field = new Field();
			field.setName(viewItem.getName());
			field.setTitle(viewItem.getTitle());
			field.setOnChange(viewItem.getOnChange());
			field.setDomain(viewItem.getDomainCondition());
			field.setReadonlyIf(viewItem.getReadonlyIf());
			field.setHideIf(viewItem.getHideIf());
			field.setRequiredIf(viewItem.getRequiredIf());

			if (viewItem.getRequired()) {
				field.setRequired(true);
			} else {
				field.setRequired(null);
			}

			if (viewItem.getReadonly()) {
				field.setReadonly(true);
			} else {
				field.setReadonly(null);
			}

			if (viewItem.getHidden()) {
				field.setHidden(true);
			} else {
				field.setHidden(null);
			}

			String widget = null;
			String selectWidget = viewItem.getWidget();
			if (viewItem.getProgressBar()) {
				widget = "SelectProgress";
			} else if (viewItem.getHtmlWidget()) {
				widget = "html";
			} else if (selectWidget != null && !selectWidget.equals("normal")) {
				widget = selectWidget;
			}
			field.setWidget(widget);

			fields.add(field);
		}

		for (Integer key : itemMap.keySet()) {
			if (key < fields.size()) {
				fields.add(key, itemMap.get(key));
			}
			else {
				fields.add(itemMap.get(key));
			}
		}

		grid.setItems(fields);
	}

	/**
	 * Method sort field according to sequence.
	 * 
	 * @param fieldList
	 *            ViewField list to sort
	 */
	public void sortFieldList(List<ViewItem> fieldList) {

		Comparator<ViewItem> viewFieldComparator = new Comparator<ViewItem>() {

			@Override
			public int compare(ViewItem item1, ViewItem item2) {

				if (item1.getSequence() < item2.getSequence()) {
					return -1;
				}

				return 0;
			}

		};

		Collections.sort(fieldList, viewFieldComparator);
	}
}
